package com.scau.jansing.thread.first.semaphore;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * 信号量
 * 限制同时访问一个资源的进程数量
 * Created by jansing on 2016/1/1.
 */
public class LearnSemaphore {
    public static void main(String[] args) {
        Pool<LearnSemaphore> pool = new Pool<>(LearnSemaphore.class, 5);
        LearnSemaphore o;
        for(int i=0; i<10; i++){
                    new Thread(new Consumer<>(i, pool)).start();
        }
        System.out.println("end");
    }
}

class Consumer<T> implements Runnable{
    private int id;
    private T t;
    private Pool<T> pool;

    public Consumer(int id, Pool<T> pool){
        this.id = id;
        this.pool = pool;
    }

    @Override
    public void run() {
        try {
            t = pool.checkOut();
            System.out.println(t + "\t\t----\t" + id);
            TimeUnit.SECONDS.sleep(2+id);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            pool.checkIn(t);
        }
    }
}

class Pool<T> {
    private int size;
    private List<T> items = new ArrayList<>();
    private volatile boolean[] checkedOut;
    private Semaphore available;
    public Pool(Class<T> classObject, int size){
        this.size = size;
        checkedOut = new boolean[size];
//        创建信号量对象，限制数量，是否公平（先到先得）
        available = new Semaphore(size, false);
        for(int i=0; i<size; i++){
            try{
                items.add(classObject.newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    public T checkOut() throws InterruptedException {
//        请求，无则阻塞
        available.acquire();
        return getItem();
    }
    public void checkIn(T t){
        if(releaseItem(t)){
//            释放
           available.release();
        }
    }
    private synchronized T getItem(){
        for(int i=0; i<size; i++){
            if(!checkedOut[i]){
                checkedOut[i] = true;
                return items.get(i);
            }
        }
        return null;
    }
    private synchronized boolean releaseItem(T item){
        int index = items.indexOf(item);
        if(index == -1){
            return false;
        }
        if(checkedOut[index]){
            checkedOut[index] = false;
            return true;
        }
        return false;
    }
}
